﻿#define TestMessage2

using SimpleTerminal.Common;
using System;
using System.Text;
using System.Threading;
using static SimpleTerminal.TerminalControl.TerminalContainer;

namespace SimpleTerminal.TerminalControl
{
    class Analyzer
    {
        //int bufferPosition = 0;

        const int bufferSize = 1024 * 128;

        //当分析线程空闲时候触发
        public AutoResetEvent ProcessIdleEvent = new AutoResetEvent(true);
        public ManualResetEvent BufferFullEvent = new ManualResetEvent(false);

        //int bufferContentSize = 0;
        // char[] buffer = new char[bufferSize];
        //char[] buffer = null;
        //int bufferWaitContentSize = 0;
        //readonly byte[] bufferWait = new byte[bufferSize];
        readonly Terminal terminal = null;

        //private object _writeLock = new object();
        private readonly UTF8Encoding encoding = new UTF8Encoding(true, false);


        public Analyzer(Terminal terminal)
        {
            this.terminal = terminal;
        }

        // int _offset = 0;
        bool UpdateUIing = false;
        public void Write(byte[] source)
        {
            terminal.BeginInvoke((EventHandler)delegate
            {

                UpdateUIing = true;
                try
                {
                Sequence(encoding.GetChars(source, 0, source.Length));
                terminal.UpdateUi();
                }
                catch (Exception e)
                {

                }
                UpdateUIing = false;
            });
            return;/*
            int totalBytesRead = 0;
            int totalBytesToRead = source.Length;
            if (source.Length <= 0)
                return;
            do
            {
                while (bufferSize - _offset == 0)
                {
                    ProcessIdleEvent.Reset();
                    ProcessIdleEvent.WaitOne();
                }
                lock (_writeLock)
                {
                    int bytes = Math.Min(bufferSize - _offset, totalBytesToRead);
                    Array.Copy(source, totalBytesRead, bufferWait, _offset, bytes);
                    totalBytesToRead -= bytes;
                    totalBytesRead += bytes;
                    _offset += bytes;
                    bufferWaitContentSize += bytes;
                }
                BufferFullEvent.Set();
            } while (totalBytesToRead > 0);
            */
        }
        /*

       public bool Receive(out char[] buffer)
       {
           bool status = true;
           while (bufferWaitContentSize == 0)
           {
               BufferFullEvent.Reset();
               BufferFullEvent.WaitOne();
           }
           lock (_writeLock)
           {
               try
               {
                   buffer = encoding.GetChars(bufferWait, 0, bufferWaitContentSize);
               }
               catch (DecoderFallbackException e)
               {
                   buffer = null;
                   status = false;
               }

             //  _offset = 0;
               bufferWaitContentSize = 0;
               ProcessIdleEvent.Set();
           }
           return status;
       }

       private bool _stop = false;

       public char Read()
       {
           if (bufferPosition == bufferContentSize)
           {
               terminal.UpdateUi();

               if (_stop)
                   throw new Exception("线程正常退出");

               while (!Receive(out buffer)) ;
               //Log.Debug("data", new string(buffer) + "len:" + buffer.Length);
               bufferContentSize = buffer.Length;
               bufferPosition = 0;
           }
           // Console.Write("写入字符" + buffer[bufferPosition].ToString() + "(" + (int)buffer[bufferPosition] + ")   ");
           return buffer[bufferPosition++];
       }

       */
        enum STATE
        {
            NORMAL,
            ESCAPE_SEQUENCES,
            OSC_SEQUENCES,
            OSC_SEQUENCES_TITLE,
            CSI_SEQUENCES,
            CSI_SEQUENCES_CONTROL,
            CHARSET_0_SEQUENCES,
            CHARSET_1_SEQUENCES,
        }

        STATE termstate = STATE.NORMAL;
        struct CSI_SEQUENCES_FLAGS
        {
            public int[] parameters;
            public int parameterCount;
            public int paramsterCode;
            public bool setMode;
        };

        struct OSC_SEQUENCES_FLAGS
        {
            public int code;
            public char[] title;
            public int p;
        }

        CSI_SEQUENCES_FLAGS CSIFlags;
        OSC_SEQUENCES_FLAGS OSCFlags;

        private bool IsDigit(char c)
        {
            return (c >= '0') && (c <= '9');
        }

        private int ToDigit(char c)
        {
            return c - 48;
        }

        private void Sequence(char[] sequences)
        {
            
            //  while (true)
            foreach (char input in sequences)
            {
                // char input = Read();

                if (input == '\x1b')
                {
                    termstate = STATE.ESCAPE_SEQUENCES;
                    continue;
                }

                switch (termstate)
                {
                    case STATE.NORMAL:
                        switch (input)
                        {
                            case '\x07':
                                //响铃
                                terminal.BeepPlay();
                                break;
                            case '\x08'://退格
                                terminal.PostCursorOffset(-1);
                                break;
                            case '\x09'://水平制表符
                                terminal.WriteTab();
                                break;
                            case '\x0b':
                                //Console.WriteLine("垂直制表符");
                                break;
                            case '\x0a':
                                terminal.MovetToNextLine();
                                break;
                            case '\x0c':
                                break;
                            case '\x0d':
                                //回车
                                terminal.MoveCursorOffset(0);
                                break;
                            case '\x0e':
                                //  Charset.Winucs.CurrentCharsetSet = 0;
                                break;
                            case '\x0f':
                                //  Charset.Winucs.CurrentCharsetSet = 1;
                                break;
                            case '\x18':
                                break;
                            default:
                                terminal.Write(input);
                                break;
                        }
                        break;
                    case STATE.CHARSET_0_SEQUENCES:
                        switch (input)
                        {
                            case 'B':
                                Charset.Winucs.G0 = Charset.Winucs.Latin1;
                                break;
                            case '0':
                                Charset.Winucs.G0 = Charset.Winucs.VT100_Graphics;
                                break;
                            case 'U':
                                Charset.Winucs.G0 = Charset.Winucs.PC;
                                break;
                            case 'K':
                                Charset.Winucs.G0 = Charset.Winucs.USER;
                                break;
                        }
                        termstate = STATE.NORMAL;
                        break;
                    case STATE.CHARSET_1_SEQUENCES:
                        switch (input)
                        {
                            case 'B':
                                Charset.Winucs.G1 = Charset.Winucs.Latin1;
                                break;
                            case '0':
                                Charset.Winucs.G1 = Charset.Winucs.VT100_Graphics;
                                break;
                            case 'U':
                                Charset.Winucs.G1 = Charset.Winucs.PC;
                                break;
                            case 'K':
                                Charset.Winucs.G1 = Charset.Winucs.USER;
                                break;
                        }
                        termstate = STATE.NORMAL;
                        break;
                    case STATE.OSC_SEQUENCES:
                        if (IsDigit(input))
                        {
                            OSCFlags.code = OSCFlags.code * 10 + ToDigit(input);
                        }
                        else if (input == ';')
                        {
                            termstate = STATE.OSC_SEQUENCES_TITLE;
                        }
                        else
                        {
                            termstate = STATE.NORMAL;
                        }
                        break;
                    case STATE.OSC_SEQUENCES_TITLE:
                        //开始读取标题 遇到\x07则结束读取过程
                        if (input == '\x07' || OSCFlags.p >= 1024)
                        {
                            termstate = STATE.NORMAL;
                            break;
                        }
                        OSCFlags.title[OSCFlags.p++] = input;
                        break;
                    case STATE.ESCAPE_SEQUENCES:
                        switch (input)
                        {
                            case '[':
                                CSIFlags = new CSI_SEQUENCES_FLAGS
                                {
                                    parameters = new int[16],
                                    parameterCount = 0,
                                    paramsterCode = 0,
                                    setMode = false,
                                };
                                termstate = STATE.CSI_SEQUENCES;
                                break;
                            case ']':
                                OSCFlags = new OSC_SEQUENCES_FLAGS
                                {
                                    code = 0,
                                    title = new char[1024],
                                    p = 0,
                                };
                                termstate = STATE.OSC_SEQUENCES;
                                break;
                            case '(':
                                termstate = STATE.CHARSET_0_SEQUENCES;
                                break;
                            case ')':
                                termstate = STATE.CHARSET_1_SEQUENCES;
                                break;
                            case 'M':
                                terminal.ReverseLinefeed();
                                termstate = STATE.NORMAL;
                                break;
                            case '7':
                                terminal.SaveCurrentState();
                                termstate = STATE.NORMAL;
                                break;
                            case '8':
                                terminal.RestoreCurrentState();
                                termstate = STATE.NORMAL;
                                break;
                            default:
                                Console.WriteLine("EscapException agin" + input.ToString());
                                termstate = STATE.NORMAL;
                                break;
                        }
                        break;
                    case STATE.CSI_SEQUENCES:
                        if (IsDigit(input))
                        {
                            CSIFlags.paramsterCode = CSIFlags.paramsterCode * 10 + ToDigit(input);
                        }
                        else if (input == '?')
                        {
                            CSIFlags.setMode = true;
                        }
                        else if (input == ';')
                        {
                            if (CSIFlags.parameterCount >= 16)
                                continue;

                            CSIFlags.parameters[CSIFlags.parameterCount++] = CSIFlags.paramsterCode;
                            CSIFlags.paramsterCode = 0;
                        }
                        else
                        {
                            if (CSIFlags.parameterCount < 16)
                            {
                                CSIFlags.parameters[CSIFlags.parameterCount++] = CSIFlags.paramsterCode;
                                CSIFlags.paramsterCode = 0;
                            }
                            switch (input)
                            {
                                case '@':
                                    //Insert the indicated # of blank characters.
                                    CSISequences_ICH(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'A':
                                    //Move cursor up the indicated # of rows.
                                    CSISequences_CUU(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'B':
                                    //Move cursor down the indicated # of rows.
                                    CSISequences_CUD(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'C':
                                    //Move cursor right the indicated # of columns.
                                    CSISequences_CUF(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'D':
                                    CSISequences_CUL(CSIFlags.parameters, CSIFlags.parameterCount);
                                    // Move cursor left the indicated # of columns.

                                    break;
                                case 'E':
                                    //Move cursor down the indicated # of rows, to column 1.
                                    break;
                                case 'F':
                                    // Move cursor up the indicated # of rows, to column 1.
                                    break;
                                case 'G':
                                    // Move cursor to indicated column in current row.
                                    CSISequences_CHA(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'H':
                                    //Move cursor to the indicated row, column (origin at 1,1).
                                    CSISequences_CUP(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'J':
                                    //Erase display (default: from cursor to end of display).
                                    // ESC[1 J: erase from start to cursor.
                                    // ESC[2 J: erase whole display.
                                    // ESC[3 J: erase whole display including scroll - back
                                    // buffer(since Linux 3.0).
                                    CSISequences_ED(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'K':
                                    // Erase line (default: from cursor to end of line).
                                    // ESC[1 K: erase from start of line to cursor.
                                    // ESC[2 K: erase whole line.
                                    CSISequences_EL(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'L':
                                    CSISequences_IL(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'M':
                                    CSISequences_DL(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'T':
                                    CSISequences_Translation(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'P':
                                    CSISequences_DCH(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'X':
                                    CSISequences_ECH(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'a':
                                    break;
                                case 'c':
                                    break;
                                case 'd':
                                    // Move cursor to the indicated row, current column.
                                    CSISequences_VPA(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'e':
                                    break;
                                case 'f':
                                    //Move cursor to the indicated row, column
                                    CSISequences_CUP(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'g':
                                    break;
                                case 'h':
                                    if (CSIFlags.setMode)
                                        CSISequences_SetModel(CSIFlags.parameters, CSIFlags.parameterCount, true);
                                    break;
                                case 'l':
                                    if (CSIFlags.setMode)
                                        CSISequences_SetModel(CSIFlags.parameters, CSIFlags.parameterCount, false);
                                    break;
                                case 'm':
                                    SGRSequence(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 'n':
                                    break;
                                case 'q':
                                    break;
                                case 'r':
                                    CSISequences_DECSTBM(CSIFlags.parameters, CSIFlags.parameterCount);
                                    break;
                                case 's':
                                    //  Save cursor location.
                                    terminal.SaveCurrentState();
                                    break;
                                case 'u':
                                    //  Restore cursor location.
                                    terminal.RestoreCurrentState();
                                    break;
                                case '`':
                                    break;
                            }
                            termstate = STATE.NORMAL;
                        }
                        break;
                }
            }
        }

        /// <summary>
        /// 移动光标到当前行的指定位置
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void CSISequences_CHA(int[] parameters, int parameterCount)
        {
            if (parameterCount == 1)
            {
                terminal.MoveCursorOffset(Math.Max(parameters[0], 1) - 1);
            }
        }
        /// <summary>
        /// 删除当前行的指定数量的字符
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void CSISequences_DCH(int[] parameters, int len)
        {
            if (len == 1)
            {
                terminal.DeleteCharacters(Math.Max(parameters[0], 1));
            }
        }

        /// <summary>
        /// 设置滚动区域
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void CSISequences_DECSTBM(int[] parameters, int len)
        {
            if (len == 2)
            {
                terminal.SetScrollingRegion(Math.Max(parameters[0], 1) - 1,
                    Math.Max(parameters[1], 1) - 1);
            }
        }


        private void CSISequences_ICH(int[] parameters, int parameterCount)
        {
            terminal.InsertBlankCharceters(Math.Max(parameters[0], 1));
        }
        private void CSISequences_ECH(int[] parameters, int parameterCount)
        {
            terminal.EraseCharacters(Math.Max(parameters[0], 1));
        }

        private void CSISequences_IL(int[] parameters, int parameterCount)
        {
            terminal.InsertBlankLine(Math.Max(parameters[0], 1));
        }

        private void CSISequences_DL(int[] parameters, int parameterCount)
        {
            terminal.DelectLine(Math.Max(parameters[0], 1));
        }

        /// <summary>
        /// 滚动平移指定的行数
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void CSISequences_Translation(int[] parameters, int len)
        {
            terminal.Translation(Math.Max(parameters[0], 1));
        }


        /// <summary>
        /// 清屏操作
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void CSISequences_SetModel(int[] parameters, int len, bool h)
        {
            if (len == 0)
            {

            }
            else
            {
                switch (parameters[0])
                {
                    case 1:
                        terminal.DECCKM = h;
                        break;
                    case 25:
                        terminal.SetCursorVisible(h);
                        break;
                    case 1000:
                        terminal.MouseReport = h;
                        break;
                    case 1049:
                        terminal.Set1049Model(h);
                        break;
                }
            }
        }

        private void CSISequences_EL(int[] parameters, int len)
        {
            terminal.EraseLine(parameters[0]);
        }
        /// <summary>
        /// 清屏操作
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>

        private void CSISequences_ED(int[] parameters, int len)
        {
            if (len == 0)
            {
                terminal.EraseDisplay(1);
            }
            else
            {
                switch (parameters[0])
                {
                    case 3:
                        terminal.EraseDisplay(3);
                        break;
                    case 2:
                        terminal.EraseDisplay(2);
                        break;
                    case 1:
                        terminal.EraseDisplay(1);
                        break;
                    default:
                        terminal.EraseDisplay(0);
                        break;
                }
            }
        }

        /// <summary>
        /// 将光标移到指定的行，当前列
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void CSISequences_VPA(int[] parameters, int len)
        {

            terminal.MoveCursorLine(Math.Max(parameters[0], 1) - 1);
            //     xterm.MoveCursorOffset(0);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void CSISequences_CUU(int[] parameters, int len)
        {

            terminal.PostCursorLine(-Math.Max(parameters[0], 1));

            //xterm.MoveCursorOffset(0);
        }


        private void CSISequences_CUD(int[] parameters, int len)
        {
            if (len == 0)
            {
                terminal.PostCursorLine(1);
            }
            else
            {
                terminal.PostCursorLine(Math.Max(parameters[0], 1));
            }
            //xterm.MoveCursorOffset(0);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void CSISequences_CUF(int[] parameters, int len)
        {
            if (len == 0)
            {
                terminal.PostCursorOffset(1);
            }
            else
            {
                terminal.PostCursorOffset(Math.Max(parameters[0], 1));
            }
        }
        private void CSISequences_CUL(int[] parameters, int len)
        {
            int c = Math.Max(parameters[0], 1);
            terminal.PostCursorOffset(-c);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void CSISequences_CUP(int[] parameters, int len)
        {
            int row = 1;
            int column = 1;
            if (len == 1)
            {
                row = Math.Max(parameters[0], 1);
            }
            else if (len > 1)
            {
                row = Math.Max(parameters[0], 1);
                column = Math.Max(parameters[1], 1);
            }

            terminal.MoveCursorLine(row - 1);
            //terminal.MoveCursorOffset(column - 1);
            terminal.MoveCursorEraseCharacters(column - 1);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameters"></param>
        /// <param name="len"></param>
        private void SGRSequence(int[] parameters, int parameterCount)
        {
            /*
             * 这里判断参数是否是;5;r;g;b的格式
             */
            if (parameters[0] == 38 && parameterCount == 5 && parameters[1] == 5)
            {
                terminal.SetForegroundRgb((byte)parameters[2], (byte)parameters[3], (byte)parameters[4]);
                return;
            }
            if (parameters[0] == 48 && parameterCount == 5 && parameters[1] == 5)
            {
                terminal.SetBackgroundRgb((byte)parameters[2], (byte)parameters[3], (byte)parameters[4]);
                return;
            }
            for (int i = 0; i < parameterCount; i++)
            {
                switch (parameters[i])
                {
                    case 0:

                        //重置显示效果
                        //terminal.ResetBackground();
                        //terminal.ResetForeground();
                        //terminal.SetCharacterStyle(0);
                        //terminal.SetHeightLight(false);
                        //terminal.ReverseVideo(false);
                        terminal.ResetCursor(true);

                        break;
                    case 1:
                        terminal.SetHeightLight(true);
                        break;
                    case 2:
                        //设置为半亮（用彩色显示器上的颜色模拟）

                        break;
                    case 4:
                        //设置下划线（在彩色显示器上用颜色模拟）
                        //（用于模拟暗淡或下划线的颜色设置为using ESC]…）
                        terminal.SetCharacterStyle(CharacterMask.Underline);
                        break;
                    case 7:
                        terminal.ReverseVideo(true);
                        break;
                    case 24:
                        terminal.UnsetCharacterStyle(CharacterMask.Underline);
                        break;
                    case 27:
                        terminal.ReverseVideo(false);
                        break;
                    case 30:
                    case 31:
                    case 32:
                    case 33:
                    case 34:
                    case 35:
                    case 36:
                    case 37:
                        terminal.SetForeground(parameters[i] - 30);
                        break;
                    case 38:
                        terminal.ResetForeground();
                        break;
                    case 39:
                        terminal.ResetForeground();
                        break;
                    case 40:
                    case 41:
                    case 42:
                    case 43:
                    case 44:
                    case 45:
                    case 46:
                    case 47:
                        terminal.SetBackground(parameters[i] - 40);
                        break;
                    case 49:
                        terminal.ResetBackground();
                        break;
                }
            }
        }




    }


}
